<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Common\Constants\PermissionTypeConstants;
use App\Common\Constants\SmsEventConstants;
use App\Common\Mapper\CommonRoleMapper;
use App\Common\Service\CommonMenuService;
use App\Common\Service\CommonRoleService;
use App\Common\Service\SmsService;
use App\CustHub\Interfaces\ChUserServiceInterface;
use App\CustHub\Mapper\ChUserMapper;
use App\CustHub\Model\ChUser;
use App\Setting\Service\SettingConfigService;
use Carbon\Carbon;
use Humx\HyperfSms\Contract\SmsInterface;
use Hyperf\Cache\Annotation\Cacheable;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;
use Hyperf\Redis\RedisFactory;
use Mine\Abstracts\AbstractService;
use Mine\Annotation\DependProxy;
use Mine\Annotation\Transaction;
use Mine\Exception\MineException;
use Mine\Helper\Str;
use Overtrue\EasySms\Exceptions\InvalidArgumentException;
use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

#[DependProxy(values: [ChUserServiceInterface::class])]
class ChUserService extends AbstractService implements ChUserServiceInterface
{
    public $mapper;

    #[Inject]
    protected CommonRoleMapper $roleMapper;

    #[Inject]
    protected ChMenuService $commonMenuService;

    #[Inject]
    protected CommonRoleService $commonRoleService;

    #[Inject]
    protected Redis $redis;

    public function __construct(ChUserMapper $mapper)
    {
        $this->mapper = $mapper;
    }

    public function getUserInfo(array $params): array
    {
        $userInfo = $this->mapper->findById($params['main_uid']);

        $result = [
            'uid' => $params['uid'],
            'main_uid' => $params['main_uid'],
            'mobile' => $userInfo->mobile
        ];

        return $result;
    }

    public function getSelectList(array $params): array
    {
        $list = ChUser::query()
            ->select(['id', 'username', 'nickname', 'parent_id'])
            ->withPermission((int)$params['uid'])
            ->orderByRaw('parent_id asc, created_at desc')
            ->get()->toArray();

        foreach ($list as $k => $item) {
            $list[$k]['nickname'] = $item['parent_id'] == 0 ? '主账号' : $item['username'];
            unset($item['parent_id']);
        }
        return $list;
    }

    /**
     * 检查手机号是否存在
     * @param string $mobile
     * @return bool
     */
    public function checkUserMobileExist(string $mobile): bool
    {
        $count = $this->mapper->getModel()
            ->where('mobile', $mobile)
            ->where('openid', '<>', '')
            ->exists();
        return $count;
    }

    /**
     * 创建账号
     * @param string $mobile
     * @param string $openid
     * @param string $unionid
     * @param string $password
     * @return void
     */
    #[Transaction]
    public function register(string $mobile, string $openid = '', string $unionid = '', string $password = '', int $inviteId = 0): void
    {
        //生成token（不加密）
        $data = [
            'account' => $mobile,
            'nickname' => '木有昵称',
            'mobile' => $mobile,
            'avatar' => 'https://pic.wikeyun.cn/common/img_avatar_def.png',
            'join_time' => time(),
            'create_time' => time(),
            'openid' => $openid,
            'unionid' => $unionid,
            'joinip' => get_client_ip(),
            'login_ip' => get_client_ip(),
            'prev_time' => Carbon::parse(time())->toDateTimeString(),
            'login_time' => Carbon::parse(time())->toDateTimeString(),
            'invite_code' => make_coupon_card(),
            'invite_id' => $inviteId,
        ];
        !empty($password) && $data['password'] = $password;
        $user = $this->mapper->getModel()::create($data);
        //设置默认角色
        $defaultRoleId = $this->roleMapper->findIdByCode('defaultRole');
        $user->roles()->sync([$defaultRoleId], false);

        container()->get(ChTagPresetService::class)->init($user->id);

        container()->get(AppService::class)->bindUser($user->id,$data['nickname']);
    }


    /**
     * 微信扫码创建用户
     * @param $wxUser
     * @param $tag
     * @param $ip
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \RedisException
     */
    public function saveUser($wxUser, $tag, $ip): bool
    {
        $inviteCode = '';
        if (str_contains($tag, 'CustHubInv_')) {
            //“Invite_”标识：后台邀请短链用户注册生成的海报，用户未关注时，进行关注后的事件推送
            $keystr = explode('CustHubInv_', $tag);
            $inviteCode = $keystr[1] ?? '';
        }

        $unionid = $wxUser['unionid'] ?? '';
        $openid = $wxUser['openid'];
        if (!empty($unionid)) {
            $user = $this->mapper->getModel()->where('unionid', $unionid)
                ->orWhere('openid', $openid)
                ->first();
        } else {
            $user = $this->mapper->getModel()
                ->orWhere('openid', $openid)
                ->first();
        }

        if (!empty($user)) {
            $data = [
                'login_ip' => $ip,
                'login_time' => Carbon::parse(time())->toDateTimeString(),
                'prev_time' => $user['login_time'],
                'openid' => $openid,
                'updated_at' => Carbon::parse(time())->toDateTimeString(),
            ];
            if (empty($user['unionid']) && !empty($unionid)) {
                $data['unionid'] = $unionid;
            }
            $this->mapper->getModel()->where('id', $user['id'])->update($data);
            $uid = $user['id'];
            $this->mapper->clearCache($uid);
        }

        //更新缓存数据
        if (str_contains($tag, 'CustHubLogIn_')) {
            $data = [
                'uuid' => $tag,
                'ip' => $ip,
                'uid' => '',
                'openid' => $openid,
                'unionid' => $unionid,
                'invite_code' => $inviteCode
            ];
            $this->redis->set($tag, $data, 86400);
        }
        return true;
    }


    /**
     * 获取用户信息.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function getInfo(?int $userId = null): array
    {
        if ($uid = (is_null($userId) ? chUser()->getId() : $userId)) {
            return $this->getCacheInfo($uid);
        }
        throw new MineException(t('system.unable_get_userinfo'), 500);
    }

    public function guide(?int $userId = null): array
    {
        if ($uid = (is_null($userId) ? chUser()->getId() : $userId)) {
            $user = $this->mapper->getModel()->find($uid);
            if (chUser()->isSuperAdmin()) {
                $routers = $this->commonMenuService->getSuperAdminRouters(PermissionTypeConstants::CUST_HUB,1);
            } else {
                $roles = $this->commonRoleService->mapper->getMenuIdsByRoleIds($user->roles()->pluck('id')->toArray());
                $ids = $this->filterMenuIds($roles);
                $routers = $this->commonMenuService->getRoutersByIds($ids,1);
            }
            return $this->changeRouters($routers);
        }
        throw new MineException(t('system.unable_get_userinfo'), 500);
    }

    /**
     * 获取缓存用户信息.
     */
//    #[Cacheable(prefix: 'commonLoginInfo', value: 'userId_#{id}', ttl: 0)]
    protected function getCacheInfo(int $id,?int $isGuide = null): array
    {
        $user = $this->mapper->getModel()->find($id);
        $user->addHidden('openid', 'unionid', 'deleted_at', 'password');
        $data['user'] = $user->toArray();
        if (chUser()->isSuperAdmin()) {
            $data['roles'] = ['superAdmin'];
            $data['routers'] = $this->commonMenuService->getSuperAdminRouters(PermissionTypeConstants::CUST_HUB,$isGuide);
            $data['codes'] = ['*'];
        } else {
            $roles = $this->commonRoleService->mapper->getMenuIdsByRoleIds($user->roles()->pluck('id')->toArray());
            $ids = $this->filterMenuIds($roles);
            $data['roles'] = $user->roles()->pluck('code')->toArray();
            $data['routers'] = $this->commonMenuService->getRoutersByIds($ids,$isGuide);
            $data['codes'] = $this->commonMenuService->mapper->getMenuCode($ids);
        }
        $data['routers'] = $this->changeRouters($data['routers']);
        return $data;
    }

    private function changeRouters($routers): array
    {
        foreach ($routers as $k => $item) {
            $routers[$k]['meta']['hideChildren'] = false;
            if ($item['path'] == '/') {
                $routers[$k]['path'] = '';
            }

            if (is_null($item['redirect'])) {
                $routers[$k]['redirect'] = '';
            }

            if (isset($item['children'])) {
                $checkChildren = array_filter($item['children'], function ($item) {
                    return $item['meta']['hidden'] == false;
                });
                empty($checkChildren) && $routers[$k]['meta']['hideChildren'] = true;
                $routers[$k]['children'] = $this->changeRouters($item['children']);
            }
        }
        return $routers;
    }

    /**
     * 过滤通过角色查询出来的菜单id列表，并去重
     * @param array $roleData
     * @return array
     */
    protected function filterMenuIds(array &$roleData): array
    {
        $ids = [];
        foreach ($roleData as $val) {
            foreach ($val['menus'] as $menu) {
                $ids[] = $menu['id'];
            }
        }
        unset($roleData);
        return array_unique($ids);
    }

    /**
     * 发送手机验证码
     * @param string $mobile
     * @param $isTest
     * @return bool|string
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \RedisException
     */
    public function sendCode(int $mainUid, $isTest = false): bool|string
    {
        $userInfo = $this->mapper->findById($mainUid);
        $mobile = $userInfo->mobile;

        $smsService = make(SmsService::class,[SmsEventConstants::CH_EDIT_PASS]);
        return $smsService->sendCode($mobile,$isTest);
    }

    /**
     * 用户修改密码
     */
    public function modifyPassword(array $params): bool
    {
        return $this->mapper->initUserPassword(user()->getId(), $params['newPassword']);
    }
}